/**
 *  JSimProd - Java simulation code for production processes
 *  Copyleft Jorge Mira Yagüe <jorge.mira.yague@gmail.com>
 *  Universidad de Alicante 2009
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package lib.simul;

import java.util.ArrayList;
import java.util.Calendar;
import lib.xml.XMLizable;

/**
 * An agenda is a collection of events with a different time, adding an event
 * changes his date, adding a millisecond until there is not any other event
 * with the same date
 * @see Event
 * @author Jorge Mira Yagüe
 */
public class Agenda implements XMLizable {

    private ArrayList<Event> events;

    /**
     * Builds an agenda
     */
    public Agenda() {
        events = new ArrayList<Event>();
    }

    /**
     * Adds an event to the agenda, increasing the date a millisecond if there
     * is an event in the same date, untill it finds a unused date
     * @param event the event to be added
     * @return true (as specified in Colletion.add)
     */
    public boolean add(Event event) {

        int index = indexOf(event.getCalendar());

        if ((index < events.size()) &&
                (events.size() != 0) &&
                (events.get(index).getCalendar().equals(event.getCalendar()))) {
            do {
                //Increase a ms the date
                event.getCalendar().add(Calendar.MILLISECOND, 1);
                index++;
            } while ((index < events.size()) &&
                    (!events.get(index).getCalendar().after(event.getCalendar())));
        }
        if (index >= events.size()) {
            events.add(event);
        } else {
            events.add(index, event);
        }
        return true;
    }

    /**
     * Returns an event that happens in the given date, or null if there is not
     * an event on the specified date
     * @param calendar the date on to get the event
     * @return an event that happens in the given date, or null if there is not
     * an event on the specified date
     */
    public Event EventAt(Calendar calendar) {
        Event tmp = events.get(indexOf(calendar));
        if (tmp.getCalendar().equals(calendar)) {
            return tmp;
        } else {
            return null;
        }
    }

    /**
     * Returns the first event on the agenda, or null if it's empty
     * @return the first event on the agenda, or null if it's empty
     */
    public Event FirstEvent() {
        return events.isEmpty() ? null : events.get(0);
    }

    /**
     * Returns the last event on the agenda, or null if it's empty
     * @return the last event on the agenda, or null if it's empty
     */
    public Event LastEvent() {
        return events.isEmpty() ? null : events.get(events.size() - 1);
    }

    /**
     * Returns the first event after a given date or null if there are events
     * after that date
     * @param calendar the date to get the event
     * @return the first event after a given date or null if there are events
     * after that date
     */
    public Event FirstEventAfter(Calendar calendar) {
        int index;
        if (!events.isEmpty()) {
            index = indexOf(calendar) + 1;
            if (index < events.size()) {
                return events.get(index);
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Returns the first event after a given date or null if there are events
     * before that date
     * @param calendar the date to get the event
     * @return the first event after a given date or null if there are events
     * before that date
     */
    public Event FirstEventBefore(Calendar calendar) {
        int index;
        Event tmp;
        if (!events.isEmpty()) {
            index = indexOf(calendar);
            tmp = events.get(index);
            if (tmp.getCalendar().equals(calendar)) {
                if (index == 0) {
                    return null;
                } else {
                    return events.get(index - 1);
                }
            } else {
                return tmp;
            }
        } else {
            return null;
        }
    }

    private int indexOf(Calendar calendar) {
        if (events.size() == 0) {
            return 0;
        }
        int start = 0;
        int end = events.size() - 1;
        int middle = 0;
        while (start <= end) {
            middle = (end + start) / 2;
            if (calendar.after(events.get(middle).getCalendar())) {
                start = middle + 1;
            } else if (calendar.before(events.get(middle).getCalendar())) {
                end = middle - 1;
            } else {
                break;
            }
        }
        return start > end ? start : middle;
    }

    public Event getEvent(int index) {
        return events.get(index);
    }

    public int size() {
        return events.size();
    }

    public ArrayList<Event> getEvents() {
        return events;
    }

    /**
     * Returns the the next free date after the given date on the agenda or the
     * given date if it's free
     * @param calendar the date to search 
     * @return the the next free date after the given date on the agenda or the
     * given date if it's free
     */
    public Calendar nextFreeSlot(Calendar calendar) {
        Calendar result = (Calendar) calendar.clone();
        int index = indexOf(calendar);

        if ((events.size() != 0) &&
                (events.get(index).getCalendar().equals(result))) {
            do {
                //Increase a ms the date
                result.add(Calendar.MILLISECOND, 1);
                index++;
            } while ((index < events.size()) &&
                    (!events.get(index).getCalendar().after(result)));
        }
        return result;
    }

    public String toXML() {
        String result="";
        if (events != null) {
            for(Event event : events){
                result += event.toXML();
            }
        }
        return result;
    }
}
